package org.teremail.mailbox;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.junit.Before;
import org.junit.Test;
import org.teremail.AllTests;
import org.teremail.message.Content;
import org.teremail.message.ContentEntity;
import org.teremail.message.Entity;
import org.teremail.message.Message;
import org.teremail.message.MultipartEntity;
import org.teremail.message.MultipartMessage;
import org.teremail.message.SimpleEntity;
import org.teremail.message.SimpleMessage;
import org.teremail.message.builder.MultipartEntityBuilder;
import org.teremail.message.builder.MultipartMessageBuilder;
import org.teremail.message.builder.SimpleEntityBuilder;
import org.teremail.message.builder.SimpleMessageBuilder;

import com.google.common.collect.ImmutableList;

public abstract class AbstractMessageDAOTestCase {
        
    protected abstract DBMessageDAO getDAO();
    private DBMessageDAO dao;
    
    @Before
    public void setUp() {
        dao = getDAO();
        dao.init();
    }
    
    @Test
    public void saveSimpleMessage() {
        
        String subject = "This is a test message";
        String to = "<bart@example.com>";
        String from = "<home@example.com>";

        SimpleMessageBuilder b = new SimpleMessageBuilder();
        b.getHeaderBuilder().setTo(to)
            .setFrom(from)
            .setSubject(subject);
        Content c = new Content(42);
        b.setContent(c);
        
        SimpleMessage sm = b.build();
        
        Entity saved = dao.save(sm);
        
        assertTrue(saved.getId() != Entity.UNSAVED_ID);
        
        SimpleMessage loaded = (SimpleMessage) dao.load(saved.getId());
        assertEquals(c, loaded.getContent());
        assertNotNull(loaded.getHeader());
    }
    
    @Test
    public void saveSimpleEntity() {
        
        SimpleEntityBuilder b = new SimpleEntityBuilder("text", "html");
        Content c = new Content("2701");
        b.setContent(c);
        
        SimpleEntity se = b.build();
        
        SimpleEntity saved = (SimpleEntity) dao.save(se);
        
        assertTrue(saved.getId() != Entity.UNSAVED_ID);      
        
        SimpleEntity loaded = (SimpleEntity) dao.load(saved.getId());
        assertEquals(c, loaded.getContent());
        assertNotNull(loaded.getContentHeader());
        assertEquals("text/html", loaded.getContentHeader().getContentType());
    }
    
    @Test
    public void saveMultipartMessage() {
        
        String subject = "This is a test message";
        String to = "<bart@example.com>";
        String from = "<home@example.com>";
        String preamble = "preamble\r\n"; 
        String epilogue = "epilogue\r\n";
        
        MultipartMessageBuilder b = new MultipartMessageBuilder();
        b.getHeaderBuilder().setTo(to).setFrom(from).setSubject(subject);
        b.setPreamble(preamble).setEpilogue(epilogue);
        
        Content c1 = new Content(1);
        Content c2 = new Content(2);
        b.addSimpleEntity().setContent(c1);
        b.addSimpleEntity("text", "html").setContent(c2);
        
        MultipartMessage message = b.build();
        
        MultipartMessage saved = (MultipartMessage) dao.save(message);
        assertTrue(saved.getId() != Entity.UNSAVED_ID);
        List<Entity> expectedE = ImmutableList.copyOf(message);
        List<Entity> actualE = ImmutableList.copyOf(saved);
        assertEquals(expectedE.size(), actualE.size());
        
        MultipartMessage loaded = (MultipartMessage) dao.load(saved.getId());
        List<Entity> childParts = ImmutableList.copyOf(loaded);
        assertEquals(2, childParts.size());
        ContentEntity e1 = (ContentEntity) childParts.get(0);
        assertEquals(c1, e1.getContent());
        ContentEntity e2 = (ContentEntity) childParts.get(1);
        assertEquals(c2, e2.getContent());
    }
    
    @Test
    public void saveMultipartEntity() {
        
        String preamble = "preamble\r\n"; 
        String epilogue = "epilogue\r\n";
        
        MultipartEntityBuilder b = new MultipartEntityBuilder();
        b.setPreamble(preamble).setEpilogue(epilogue);
        
        Content c1 = new Content(1);
        Content c2 = new Content(2);
        b.addSimpleEntity(c1);
        b.addSimpleEntity(c2);
        
        MultipartEntity entity = b.build();
        
        MultipartEntity saved = (MultipartEntity) dao.save(entity);
        
        assertTrue(saved.getId() != Entity.UNSAVED_ID);
        List<Entity> expectedE = ImmutableList.copyOf(entity.iterator());
        List<Entity> actualE = ImmutableList.copyOf(saved.iterator());
        assertEquals(expectedE.size(), actualE.size());
        
        MultipartEntity loaded = (MultipartEntity) dao.load(saved.getId());
        List<Entity> childParts = ImmutableList.copyOf(loaded.iterator());
        assertEquals(2, childParts.size());
        ContentEntity e1 = (ContentEntity) childParts.get(0);
        assertEquals(c1, e1.getContent());
        ContentEntity e2 = (ContentEntity) childParts.get(1);
        assertEquals(c2, e2.getContent());        
    }
    
    @Test
    public void loadMultipleEntities() throws Exception {
        
        List<String> ids = new ArrayList<String>();
        for (int i = 0; i < 5; i++) {
            Entity m = dao.save(AllTests.getMultipartMessage("<homer@example.com>", 
                    "<bart@example.com>", "message-" + i));
            ids.add(m.getId());
        }
        
        for (int i = 0; i < 5; i++) {
            dao.save(AllTests.getSimpleMessage("<bart@example.com>", 
                    "<homer@example.com>", "other-" + i));
        }
        
        Map<String,Message> ms = dao.loadMessages(ids);
        
        assertEquals(5, ms.size());
        for (Message m : ms.values()) {
            assertEquals("<homer@example.com>", m.getHeader().getFrom());
            assertEquals("<bart@example.com>", m.getHeader().getTo());
        }
        assertTrue(ms.keySet().containsAll(ids));
    }
    
}
